4 // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
6 // The use and distribution terms for this software are contained in the file
7 // named license.txt, which can be found in the root of this distribution.
8 // By using this software in any fashion, you are agreeing to be bound by the
9 // terms of this license.
11 // You must not remove this notice, or any other, from this software.
15 // COMMAND.C - NMAKE 'command line' handling routines
18 // Module contains routines to handle NMAKE 'command line' syntax. NMAKE can be
19 // optionally called by using the syntax 'NMAKE @commandfile'. This allows more
20 // flexibility and preents a way of getting around DOS's 128-byte limit on the
21 // length of a command line. Additionally, it saves keystrokes for frequently
22 // run commands for NMAKE.
29 void addArgument(char*,unsigned,char***);
30 void processLine(char*,unsigned*,char***);
31 void tokenizeLine(char*,unsigned*,char***);
35 // arguments: name pointer to name of command file to read
37 // actions: opens command file
38 // reads in lines and calls processLine() to
39 // break them into tokens and to build
40 // an argument vector (a la argv[])
41 // calls parseCommandLine() recursively to process
42 // the accumulated "command line" arguments
43 // frees space used by the arg vector
45 // modifies: makeFiles in main() by modifying contents of parameter list
46 // makeTargets in main() by modifying contents of targets parameter
49 // notes: function is not ANSI portable because it uses fopen()
50 // with "rt" type and text mode is a Microsoft extension
59 **vector
; // local versions of arg vector
60 unsigned count
= 0; // count
63 if (!(file
= FILEOPEN(name
,"rt")))
64 makeError(0,CANT_OPEN_FILE
,name
);
65 vector
= NULL
; // no args yet
66 while (fgets(buf
,MAXBUF
,file
)) {
69 // if we didn't get the whole line, OR the line ended with a backSlash
71 if ((n
== MAXBUF
-1 && buf
[n
-1] != '\n') ||
72 (buf
[n
-1] == '\n' && buf
[n
-2] == '\\')
74 if (buf
[n
-2] == '\\' && buf
[n
-1] == '\n') {
75 // Replace \n by \0 and \\ by a space; Also reset length
85 processLine(s
,&count
,&vector
); // separate into args
90 if (fclose(file
) == EOF
)
91 makeError(0, ERROR_CLOSING_FILE
, name
);
93 parseCommandLine(count
,vector
); // evaluate the args
94 while (count
--) // free the arg vector
96 FREE(vector
[count
]); // NULL entries mean that the space the
97 } // entry used to pt to is still in use
102 // arguments: s pointer to readCommandFile()'s buffer
103 // holding line so far
104 // n pointer to readCommandFile()'s count of
107 // actions: keeps reading in text until it sees a newline
108 // or the end of file
109 // reallocs space for the old buffer plus the
110 // contents of the new buffer each time
111 // appends new buffer's text to existing text
113 // modifies: s readCommandFile()'s text buffer by realloc'ing
114 // more space for incoming text
115 // n readCommandFile()'s count of bytes in s
128 while ((*s
)[*n
-1] != '\n') { // get rest of line
129 if (!fgets(t
,MAXBUF
,file
))
132 if (t
[temp
-2] == '\\' && t
[temp
-1] == '\n') {
133 //Replace \n by \0 and \\ by a space; Also reset length
139 *s
= (char *) REALLOC(*s
,*n
+1); // + 1 for NULL byte
141 makeError(line
, MACRO_TOO_LONG
);
162 BOOL allocFlag
= FALSE
;
164 if (!(t
= _tcschr(s
,'"'))) { // no quoted strings,
165 tokenizeLine(s
,count
,vector
); // just standard fare
167 // There are two kinds of situations in which quotes can occur:
168 // 1. "FOO = bar baz"
171 if ((t
== s
) || (*(t
-1) != '=')) {
173 *t
++ = '\0'; // quoted macrodef
174 tokenizeLine(s
,count
,vector
); // get tokens before "
178 for (u
= t
; u
> s
; --u
) // find the beginning of the macro name
179 if (*u
== ' ' || *u
== '\t' || *u
== '\n')
184 tokenizeLine(s
, count
, vector
);
191 for (u
= t
; *u
; ++u
) { // look for closing "
192 if (*u
== '"') { // need " and not ""
197 *u
++ = '\0'; // terminate macrodef
198 addArgument(t
,*count
,vector
); // treat as one arg
200 processLine(u
+1,count
,vector
); // recurse on rest of line
205 && WHITESPACE(*(u
-1))
206 && (*(u
+1) == '\n')) { // \n always last char
207 *u
= '\0'; // 2 chars go to 1
208 m
= (n
= n
-2); // adjust length count
213 getRestOfLine(&t
,&n
); // get some more text
214 u
= t
+ m
; // reset u & continue looping
218 if (u
== t
+ n
) { // if at end of line
219 makeError(0,SYNTAX_NO_QUOTE
); // and no ", error
231 // arguments: s pointer to readCommandFile()'s buffer
232 // holding "command line" to be tokenized
233 // count pointer to readCommandFile()'s count of
234 // "command line" arguments seen so far
235 // vector pointer to readCommandFile()'s vector of
236 // pointers to character strings
238 // actions: breaks the line in s into tokens (command line
239 // arguments) delimited by whitespace
240 // adds each token to the argument vector
241 // adjusts the argument counter
243 // modifies: vector readCommandFile()'s vector of pointers to
244 // "command line argument" strings (by modifying
245 // the contents of the parameter pointer, vector)
246 // count readCommandFile()'s count of the arguments in
247 // the vector (by modifying the contents of the
248 // parameter pointer, count)
250 // If the user ever wants '@' to be part of an argument in a command file,
251 // he has to enclose that argument in quotation marks.
254 tokenizeLine( // gets args delimited
255 char *s
, // by whitespace and
256 unsigned *count
, // constructs an arg
257 char **vector
[] // vector
262 if ((t
= _tcschr(s
,'\\'))) {
263 if (WHITESPACE(*(t
-1)) && (*(t
+1) == '\n')) {
268 for (t
= _tcstok(s
," \t\n"); t
; t
= _tcstok(NULL
," \t\n")) {
270 makeError(0,SYNTAX_CMDFILE
,t
+1);
271 break; // should we keep on parsing here?
273 addArgument(t
,*count
,vector
);
281 // arguments: s pointer to text of argument to be added
282 // to the "command line argument" vector
283 // count pointer to readCommandFile()'s count of
284 // "command line" arguments seen so far
285 // vector pointer to readCommandFile()'s vector of
286 // pointers to character strings
288 // actions: allocates space in the vector for the new argument
289 // allocates space for argument string
290 // makes vector entry point to argument string
292 // modifies: vector readCommandFile()'s vector of pointers to
293 // "command line argument" strings (by modifying
294 // the contents of the parameter pointer, vector)
295 // (count gets incremented by caller)
297 // To keep from fragmenting memory by doing many realloc() calls for very
298 // small amounts of space, we get memory in small chunks and use that until
299 // it is depleted, then we get another chunk . . . .
302 addArgument( // puts s in vector
309 *vector
= (char**) allocate(CHUNKSIZE
*sizeof(char*));
310 } else if (!(count
% CHUNKSIZE
)) {
311 *vector
= (char**) REALLOC(*vector
,(count
+CHUNKSIZE
)*sizeof(char*));
313 makeError(0,OUT_OF_MEMORY
);
316 (*vector
)[count
] = makeString(s
);